/**
 * @file wa_pid.c
 *
 */

 /*********************
  *      INCLUDES
  *********************/
#include "wa_pid.h"
#include "wa_wm.h"
#include "wa_tick.h"
/*********************
*      DEFINES
*********************/

/**********************
*      TYPEDEFS
**********************/

/**********************
*  STATIC PROTOTYPES
**********************/

/**********************
*  STATIC VARIABLES
**********************/
static wa_pid_state _pid_state;
/**********************
*      MACROS
**********************/

/**********************
*   GLOBAL FUNCTIONS
**********************/

void wa_pid_module_init(void)
{
	wm_context.pid.act_obj = NULL;
	wm_context.pid.last_obj = NULL;
	wm_context.pid.last_x = 0;
	wm_context.pid.last_y = 0;
	wm_context.pid.obj_draging = wa_nok;
	wm_context.pid.obj_long_pressing = wa_nok;
	wm_context.pid.press_timestamp = 0;
	wm_context.pid.longpress_rep_timestamp = 0;
	wm_context.pid.vect_x = 0;
	wm_context.pid.vect_y = 0;
	wm_context.pid.drag_range_out = wa_nok;
	wm_context.pid.drag_sum_x = 0;
	wm_context.pid.drag_sum_y = 0;
	wm_context.pid.enable = wa_nok;

	_pid_state.x = -1;
	_pid_state.y = -1;
	_pid_state.state = wa_nok;
}

/**
 * store the pid state
 * @param pid_state
 */
void wa_pid_store_state(wa_pid_state* pid_state)
{
	if (pid_state != NULL)
	{
		_pid_state.x = pid_state->x;
		_pid_state.y = pid_state->y;
		_pid_state.state = pid_state->state;
	}
}

/**
 * get pid state
 * @param pid_state
 */
void wa_pid_get_state(wa_pid_state* pid_state)
{
	if (pid_state != NULL)
	{
		pid_state->x = _pid_state.x;
		pid_state->y = _pid_state.y;
		pid_state->state = _pid_state.state;
	}
}

/**
 * pid enable
 * @param state
 */
void wa_pid_enable(wa_result state)
{
	wm_context.pid.enable = state;
}

/**
 * check pid is enable or not
 * @param state
 */
wa_result wa_pid_is_enable(void)
{
	return wm_context.pid.enable;
}

/**
 * search obj which is click
 * @param pid_state
 * @param obj start to find
 * @return click obj
 */
wa_obj* wa_pid_search_obj(wa_pid_state* pid_state, wa_obj* obj)
{
	wa_obj* obj_found = NULL;
	wa_obj* child;
	wa_point point;

	point.x = pid_state->x;
	point.y = pid_state->y;
	if (wa_area_is_point_on(&(obj->rect), &point) == wa_ok)
	{
		for (child = wa_ll_get_head(&(obj->child_ll)); child != NULL; child = wa_ll_get_next(&(obj->child_ll), child))
		{
			obj_found = wa_pid_search_obj(pid_state, child);
			if (obj_found != NULL)
			{
				break;
			}
		}
		if ((obj_found == NULL) && (obj->flag & WA_CF_ENABLE))
		{
			wa_obj* obj_hide;
			obj_hide = obj;
			while (obj_hide != NULL)
			{
				if (!(obj_hide->flag & WA_CF_SHOW))
				{
					break;
				}
				obj_hide = wa_obj_get_parent(obj_hide);
			}
			if (obj_hide == NULL)
			{
				obj_found = obj;
			}
		}
	}
	return obj_found;
}

/**
 * handle drag event
 * @param pid_state
 */
void wa_pid_drag(wa_pid_state* pid_state)
{
	wa_obj* drag_obj;

	drag_obj = wm_context.pid.act_obj;
	while ((drag_obj->flag & WA_CF_DRAG_PARENT) && drag_obj != NULL)
	{
		drag_obj = wa_obj_get_parent(drag_obj);
	}
	if (drag_obj == NULL)
	{
		return;
	}
	if (!(drag_obj->flag & WA_CF_DRAG))
	{
		return;
	}
	if (wm_context.pid.drag_range_out == wa_nok)
	{
		wm_context.pid.drag_sum_x += wm_context.pid.vect_x;
		wm_context.pid.drag_sum_y += wm_context.pid.vect_y;
		if (WA_MATH_ABS(wm_context.pid.drag_sum_x) >= WA_PID_DRAG_LIMIT
			|| WA_MATH_ABS(wm_context.pid.drag_sum_y) >= WA_PID_DRAG_LIMIT)
		{
			wm_context.pid.drag_range_out = wa_ok;
		}
	}
	if (wm_context.pid.drag_range_out == wa_ok)
	{
		if (wm_context.pid.vect_x != 0 || wm_context.pid.vect_y != 0)
		{
			int act_x, act_y;
			act_x = wa_obj_get_rel_x(drag_obj);
			act_y = wa_obj_get_rel_y(drag_obj);
			if (drag_obj->flag & WA_CF_DRAG_HOR)
			{
				wa_obj_set_pos(drag_obj, act_x + wm_context.pid.vect_x, act_y);
			}
			else if (drag_obj->flag & WA_CF_DRAG_VER)
			{
				wa_obj_set_pos(drag_obj, act_x, act_y + wm_context.pid.vect_y);
			}
			else
			{
				wa_obj_set_pos(drag_obj, act_x + wm_context.pid.vect_x, act_y + wm_context.pid.vect_y);
			}
			if (act_x != wa_obj_get_rel_x(drag_obj) || act_y != wa_obj_get_rel_y(drag_obj))
			{
				if (wm_context.pid.obj_draging == wa_nok)
				{
					wa_wm_send_sign_nodata(drag_obj, WA_SIGNAL_DRAG_BEGIN);
				}
				wm_context.pid.obj_draging = wa_ok;
			}
		}
	}
}

/**
 * handle drag end event
 */
void wa_pid_drag_end(void)
{
	wa_obj* drag_obj;
	if (wm_context.pid.obj_draging == wa_nok)
	{
		return;
	}
	drag_obj = wm_context.pid.act_obj;

	while ((drag_obj->flag & WA_CF_DRAG_PARENT) && drag_obj != NULL)
	{
		drag_obj = wa_obj_get_parent(drag_obj);
	}
	if (drag_obj == NULL)
	{
		return;
	}
	wm_context.pid.obj_draging = wa_nok;
	wa_wm_send_sign_nodata(drag_obj, WA_SIGNAL_DRAG_END);
}

/**
 * handle preess event
 * @param pid_state
 */
void wa_pid_proc_press(wa_pid_state* pid_state)
{
	wa_obj* pr_obj;

	pr_obj = wm_context.pid.act_obj;
	if (wm_context.pid.act_obj == NULL)
	{
		pr_obj = wa_pid_search_obj(pid_state, &top_scr_obj);
	}
	else if (wm_context.pid.obj_draging == wa_nok)
	{
		pr_obj = wa_pid_search_obj(pid_state, &top_scr_obj);
	}
	else
	{
	}
	if (pr_obj != wm_context.pid.act_obj)
	{
		wm_context.pid.last_x = pid_state->x;
		wm_context.pid.last_y = pid_state->y;

		if (wm_context.pid.act_obj != NULL)
		{
			wa_wm_send_sign_nodata(wm_context.pid.act_obj, WA_SIGNAL_PRESS_LOST);
		}

		if (pr_obj != NULL)
		{
			wa_obj* top = NULL;
			wa_obj* obj_i = pr_obj;

			wm_context.pid.obj_draging = wa_nok;
			wm_context.pid.obj_long_pressing = wa_nok;
			wm_context.pid.press_timestamp = wa_tick_get();
			wm_context.pid.longpress_rep_timestamp = 0;
			wm_context.pid.drag_range_out = wa_nok;
			wm_context.pid.drag_sum_x = 0;
			wm_context.pid.drag_sum_y = 0;

			while (obj_i != NULL)
			{
				if (obj_i->flag & WA_CF_CLICK_TOP)
				{
					top = obj_i;
				}
				obj_i = wa_obj_get_parent(obj_i);
			}
			if (top != NULL)
			{
				wa_obj* par;
				par = wa_obj_get_parent(top);
				wa_ll_chg_list(&par->child_ll, &par->child_ll, top);
				wa_obj_invalidate(top);
			}

			wa_wm_send_sign_nodata(pr_obj, WA_SIGNAL_PRESSED);
		}
	}

	wm_context.pid.act_obj = pr_obj;
	wm_context.pid.last_obj = wm_context.pid.act_obj;
	wm_context.pid.vect_x = pid_state->x - wm_context.pid.last_x;
	wm_context.pid.vect_y = pid_state->y - wm_context.pid.last_y;

	if (wm_context.pid.act_obj != NULL)
	{
		wa_wm_send_sign_nodata(wm_context.pid.act_obj, WA_SIGNAL_PRESSING);

		wa_pid_drag(pid_state);

		if (wm_context.pid.obj_draging == wa_nok && wm_context.pid.obj_long_pressing == wa_nok)
		{
			if (wa_tick_get_elap(wm_context.pid.press_timestamp) > WA_PID_LONG_PRESS_TIME)
			{
				wa_wm_send_sign_nodata(wm_context.pid.act_obj, WA_SIGNAL_LONG_PRESS);
				wm_context.pid.obj_long_pressing = wa_ok;
				wm_context.pid.longpress_rep_timestamp = wa_tick_get();
			}
		}
		if (wm_context.pid.obj_draging == wa_nok && wm_context.pid.obj_long_pressing == wa_ok)
		{
			if (wa_tick_get_elap(wm_context.pid.longpress_rep_timestamp) > WA_PID_LONG_PRESS_REP_TIME)
			{
				wa_wm_send_sign_nodata(wm_context.pid.act_obj, WA_SIGNAL_LONG_PRESS_REP);
				wm_context.pid.longpress_rep_timestamp = wa_tick_get();
			}
		}
	}
}

/**
 * handle pid event
 * @param pid_state
 */
void wa_pid_proc_release(void)
{
	if (wm_context.pid.act_obj != NULL)
	{
		wa_wm_send_sign_nodata(wm_context.pid.act_obj, WA_SIGNAL_RELEASED);
		wa_pid_drag_end();
		wm_context.pid.act_obj = NULL;
		wm_context.pid.press_timestamp = 0;
		wm_context.pid.longpress_rep_timestamp = 0;
		wm_context.pid.obj_long_pressing = wa_nok;
	}
}

/**
 * handle point event
 */
void wa_pid_proc_point(wa_pid_state* pid_state)
{
	if (_pid_state.state == wa_ok)
	{
		wa_pid_proc_press(pid_state);
	}
	else
	{
		wa_pid_proc_release();
	}
	wm_context.pid.last_x = pid_state->x;
	wm_context.pid.last_y = pid_state->y;
}

/**********************
 *   STATIC FUNCTIONS
 **********************/
